home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Dev / Oberon / texts / Oberon2.OOP.Text < prev    next >
Text File  |  1993-11-02  |  27KB  |  602 lines

  1. OBJECT-ORIENTED PROGRAMMING IN OBERON-2
  2.  
  3.  
  4. Hanspeter M„ssenb„ck
  5. ETH Z…rich, Institut f…r Computersysteme
  6.  
  7.  
  8. ABSTRACT
  9.  
  10. Oberon-2 is a refined version of Oberon developed at ETH. It introduces
  11. type-bound procedures, read-only export of data, and open array variables.
  12. The For statement is reintroduced. This paper concentrates on type-bound
  13. procedures which make Oberon-2 an object-oriented language with
  14. dynamically-bound messages and strong type checking at compile time.
  15. Messages can also be sent as data packets (extensible records) that are
  16. passed to a handler procedure and are interpreted at run time. This is as
  17. flexible as the Smalltalk message dispatching mechanism. Objects carry type
  18. information at run time which allows dynamic binding of messages, run time
  19. type tests, and the implementation of persistent objects. Oberon-2 is
  20. available on various machines.
  21.  
  22.  
  23. OVERVIEW
  24.  
  25. In 1987, Wirth defined the language Oberon [1]. Compared with its
  26. predecessor Modula-2, Oberon is smaller and cleaner, and it supports type
  27. extension which is a prerequisite for object-oriented programming. Type
  28. extension allows the programmer to extend an existing record type by adding
  29. new fields while preserving the compatibility between the old and the new
  30. type. Operations on a type, however, have to be implemented as ordinary
  31. procedures without syntactic relation to that type. They cannot be redefined
  32. for an extended type. Therefore dynamically-bound messages (which are vital
  33. for object-oriented programming) are not directly supported by Oberon,
  34. although they can be implemented via message records (see below).
  35.  
  36. Compared to Oberon, Oberon-2 [2] provides type-bound procedures (methods),
  37. read-only export of data, and open array variables. The For statement is
  38. reintroduced after having been eliminated in the step from Modula-2 to
  39. Oberon. This paper concentrates on type-bound procedures and the use of
  40. Oberon-2 for object-oriented programming. The other facilities are described
  41. in the Oberon-2 language report.
  42.  
  43. Type-bound procedures are operations applicable to variables of a record or
  44. pointer type. They are syntactically associated with that type and can
  45. therefore easily be identified as its operations. They can be redefined for
  46. an extended type and are invoked using dynamic binding. Type-bound
  47. procedures together with type extension make Oberon-2 a true object-oriented
  48. language with dynamically-bound messages and strong type checking at compile
  49. time. Oberon-2 is the result of three years experience of using Oberon and
  50. its experimental offspring Object Oberon [3]. Object-oriented concepts were
  51. integrated smoothly into Oberon without sacrificing the conceptual
  52. simplicity of the language.
  53.  
  54. Object-oriented programming is based on three concepts: data abstraction,
  55. type extension and dynamic binding of a message to the procedure that
  56. implements it. All these concepts are supported by Oberon-2. We first
  57. discuss type extension since this is perhaps the most important of the three
  58. notions, and then turn to type-bound procedures, which allow data
  59. abstraction and dynamic binding.
  60.  
  61.  
  62. TYPE EXTENSION
  63.  
  64. Type extension was introduced by Wirth in Oberon. It allows the programmer
  65. to derive a new type from an existing one by adding data fields to it.
  66. Consider the declarations
  67.  
  68.   TYPE
  69.     PointDesc  = RECORD x, y: INTEGER END;
  70.     PointDesc3D  = RECORD (PointDesc) z: INTEGER END;
  71.  
  72.     Point  = POINTER TO PointDesc;
  73.     Point3D  = POINTER TO PointDesc3D;
  74.  
  75.     PointXYZ  = POINTER TO PointDescXYZ;
  76.     PointDescXYZ  = RECORD x, y, z: INTEGER END;
  77.  
  78. PointDesc3D is an extension of PointDesc (specified by the type name in
  79. parentheses that follows the symbol RECORD). It starts with the same fields
  80. as PointDesc but contains an additional field z. Conversely, PointDesc is
  81. called the base type of PointDesc3D. The notion of type extension also
  82. applies to pointers. Point3D is an extension of Point and Point is the base
  83. type of Point3D. Type extension is also called inheritance because one can
  84. think of PointDesc3D as "inheriting" the fields x and y from PointDesc.
  85.  
  86. The crucial point about type extension is that Point3D is compatible with
  87. Point, while PointXYZ is not (though it also points to a record with the
  88. fields x and y). If p is of type Point and p3 is of type Point3D the
  89. assignment
  90.  
  91.   p := p3
  92.  
  93. is legal since p3 is an (extended) Point and therefore assignment compatible
  94. with p, which is a Point. The reverse assignment p3 := p is illegal since p
  95. is only a Point but not a Point3D like p3. The same compatibility rules
  96. apply to records.
  97.  
  98. Objects which are pointers or records have both a static type and a dynamic
  99. type. The static type is the type which the object is declared of. The
  100. dynamic type is the type which the object has at run time. It may be an
  101. extension of its static type. After the assignment p := p3 the dynamic type
  102. of p is Point3D, while its static type is still Point. That means that the
  103. field p3^.z is still part of the block that p points to, but it cannot be
  104. accessed via p since the static type of p does not contain a field p^.z
  105. (Figure 1).
  106.  
  107.  
  108.   Figure 1.  Assignment between the extended object and the base object
  109.  
  110. Objects like p are polymorphic, i.e. they may assume various types at run
  111. time. The actual type an object has at run time can be examined with a type
  112. test:
  113.  
  114.   p IS Point3D
  115.  
  116. yields TRUE if the dynamic type of p is Point3D (or an extension of it) and
  117. FALSE otherwise. A type guard
  118.  
  119.   p(Point3D)
  120.  
  121. asserts (i.e., tests at run time) that the dynamic type of p is Point3D (or
  122. an extension of it). If so, the designator p(Point3D) is regarded as having
  123. the static type Point3D. If not, the program is aborted. Type guards allow
  124. the treatment of p as a Point3D object. Therefore the following assignments
  125. are possible: p(Point3D)^.z := 0; p3 := p(Point3D);
  126.  
  127. For objects of a record type, the static and the dynamic types are usually
  128. the same. If pd is of type PointDesc and pd3 is of type PointDesc3D, the
  129. assignment pd := pd3 does not change the dynamic type of pd. Only the fields
  130. pd3.x and pd3.y are moved to pd, and the dynamic type of pd remains
  131. PointDesc. The compatibility between records is of minor importance except
  132. when pd is a formal variable parameter and pd3 is its corresponding actual
  133. parameter. In this case the dynamic type of pd is Point3D and the component
  134. pd3^.z is not stripped off.
  135.  
  136. The motivation for type extension is that an algorithm which works with type
  137. Point can also work with any of its extensions. For example, the procedure
  138.  
  139.   PROCEDURE Move (p: Point; dx, dy: INTEGER);
  140.   BEGIN INC(p.x, dx); INC(p.y, dy)
  141.   END Move;
  142.  
  143. can be called not only as Move(p, dx, dy) but also as Move(p3, dx, dy).
  144.  
  145.  
  146. TYPE-BOUND PROCEDURES
  147.  
  148. Type-bound procedures serve to implement abstract data types with
  149. dynamically bound operations. An abstract data type is a user-defined type
  150. which encapsulates private data together with a set of operations that can
  151. be used to manipulate this data. In Modula-2 or in Oberon an abstract data
  152. type is implemented as a record type and a set of procedures. The
  153. procedures, however, are syntactically unrelated to the record, which
  154. sometimes makes it hard to identify the data and the operations as an
  155. entity.
  156.  
  157. In Oberon-2, procedures can be connected to a data type explicitly. Such
  158. procedures are called type-bound. The interface of an abstract data type for
  159. texts may look like this:
  160.  
  161.   TYPE
  162.     Text = POINTER TO TextDesc;
  163.     TextDesc = RECORD
  164.       data: ... (*(hidden) text data*)
  165.       PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);
  166.       PROCEDURE (t: Text) Delete (from, to: LONGINT);
  167.       PROCEDURE (t: Text) Length (): LONGINT;
  168.     END;
  169.  
  170. This gives a nice overview showing which operations can be applied to
  171. variables of type Text. However, it would be unwise to implement the
  172. operations directly within the record since that would clutter up the
  173. declarations with code. In fact, the above view of Text was extracted from
  174. the source code with a browser tool. The actual Oberon-2 program looks like
  175. this:
  176.  
  177.   TYPE
  178.     Text = POINTER TO TextDesc;
  179.     TextDesc = RECORD
  180.       data:  (*(hidden) text data*)
  181.     END;
  182.  
  183.   PROCEDURE (t: Text) Insert (string: ARRAY OF CHAR; pos: LONGINT);
  184.   BEGIN ...
  185.   END Insert;
  186.  
  187.   PROCEDURE (t: Text) Delete (from, to: LONGINT);
  188.   BEGIN ...
  189.   END Delete;
  190.  
  191.   PROCEDURE (t: Text) Length (): LONGINT;
  192.   BEGIN ...
  193.   END Length;
  194.  
  195. This notation allows the programmer to declare the procedures in arbitrary
  196. order and after the type and variable declarations, eliminating the problem
  197. of forward references. The procedures are associated with a record by the
  198. type of a special formal parameter (t: Text) written in front of the
  199. procedure name. This parameter denotes the operand to which the operation is
  200. applied (or the receiver of a message, as it is called in object-oriented
  201. terminology). Type-bound procedures are considered local to the record to
  202. which they are bound. In a call they must be qualified with an object of
  203. this type, e.g.
  204.  
  205.   txt.Insert("Hello", 0)
  206.  
  207. We say that the message Insert is sent to txt, which is called the receiver
  208. of the message. The variable txt serves two purposes: it is passed as an
  209. actual parameter to t and it is used to select the procedure Insert bound to
  210. type Text.
  211.  
  212. If Text is extended, the procedures bound to it are automatically also bound
  213. to the extended type. However, they can be redefined by a new procedure with
  214. the same name (and the same parameter list), which is explicitly bound to
  215. the extended type. Let's assume that we want to have a more sophisticated
  216. type StyledText which not only maintains ASCII text but also style
  217. information. The operations Insert and Delete have to be redefined since
  218. they now also have to update the style data, whereas the operation Length is
  219. not affected by styles and can be inherited from Text without redefinition.
  220.  
  221.   TYPE
  222.     StyledText = POINTER TO StyledTextDesc;
  223.     StyledTextDesc = RECORD (TextDesc)
  224.       style:  ... (*(hidden) style data*)
  225.     END;
  226.  
  227.   PROCEDURE (st: StyledText) Insert (string: ARRAY OF CHAR; pos: LONGINT);
  228.   BEGIN
  229.     ... update style data ...
  230.     st.Insert^ (string, pos)
  231.   END Insert;
  232.  
  233.   PROCEDURE (st: StyledText) Delete (from, to: LONGINT);
  234.   BEGIN
  235.     ... update style data ...
  236.     st.Delete^ (from, to)
  237.   END Delete;
  238.  
  239. We do not want to rewrite Insert and Delete completely but only want to
  240. update the style data and let the original procedures bound to Text do the
  241. rest of the work. In a procedure bound to type T, a procedure bound to the
  242. base type of T is called by appending the symbol ^ to the procedure name in
  243. a call (st.Insert^ (string, pos)).
  244.  
  245.  
  246. Dynamic binding
  247.  
  248. Because of the compatibility between a type and its extensions, a variable
  249. st of type StyledText can be assigned to a variable t of type Text. The
  250. message t.Insert then invokes the procedure Insert which is bound to
  251. StyledText, although t has been declared of type Text. This is called
  252. dynamic binding: the called procedure is the one which is bound to the
  253. dynamic type of the receiver.
  254.  
  255. Polymorphism and dynamic binding are the cornerstones of object-oriented
  256. programming. They allow viewing an object as an abstract entity which may
  257. assume various concrete shapes at run time. In order to apply an operation
  258. to such an object, one does not have to distinguish between its variants.
  259. One rather sends a message telling the object what to do. The object
  260. responds to the message by invoking that procedure which implements the
  261. operation for the dynamic type of the receiver.
  262.  
  263. In Oberon-2, all type-bound procedures are called using dynamic binding. If
  264. static binding is desired (which is slightly more efficient), ordinary
  265. procedures can be used. However, one must be aware that statically-bound
  266. procedures cannot be redefined.
  267.  
  268.  
  269. Information hiding
  270.  
  271. One important property of abstract data types is information hiding, i.e.
  272. the implementation of private data should not be visible to clients. It
  273. seems as if information hiding is violated in Oberon-2 since all record
  274. components can be accessed if they are qualified with an object of that
  275. record type. However, hiding components is not the business of records; it
  276. is the business of modules. A module can export record fields (and
  277. type-bound procedures) selectively. In client modules only the exported
  278. components are visible. If none of the record fields is exported the private
  279. data of the record is hidden to clients.
  280.  
  281.  
  282. Notation
  283.  
  284. Object-oriented languages differ in the notations they use for classes and
  285. type-bound procedures. We want to explain why we chose the above notation in
  286. Oberon-2.
  287.  
  288. Classes. We refrained from introducing classes but rather expressed them by
  289. the well-known concept of records. Classes are record types with procedures
  290. bound to them.
  291.  
  292. Methods. Methods are expressed by type-bound procedures. The fact that their
  293. invocation is driven by the dynamic type of an object is reflected by the
  294. qualification with an explicit receiver parameter. In a call, the actual
  295. receiver is written in front of the message name (x.P); therefore the formal
  296. receiver is also declared in front of the procedure name (PROCEDURE (x: T) P
  297. (...)).
  298.  
  299. We refrained from duplicating the headers of bound procedures in record
  300. declarations as it is done in C++ [6] and Object-Pascal [8]. This keeps
  301. declarations short and avoids unpleasant redundancy. Changes to a procedure
  302. header would otherwise have to be made at two places and the compiler would
  303. have to check the equality of the headers. If the programmer wants to see
  304. the record together with all its procedures, he uses a browser to obtain the
  305. information. We believe that the working style of programmers has changed in
  306. recent years. Programs are written more interactively and high performance
  307. tools can be used to collect information that had to be written down
  308. explicitly in former days.
  309.  
  310. The procedures bound to a type can be declared in any order. They can even
  311. be mixed with procedures bound to a different type. If procedures had to be
  312. declared within a type declaration, indirect recursion between procedures
  313. bound to different types would make awkward forward declarations necessary
  314. for one-pass compilation.
  315.  
  316. Receiver. In most object-oriented languages the receiver of a message is
  317. passed as an implicit parameter that can be accessed within a method by a
  318. predeclared name such as self or this. The data of a class can be accessed
  319. in a method without qualification. For example, in C++ the method Delete
  320. would look like this:
  321.  
  322.   void Text::Delete (int from, to) {
  323.     length = length - (to-from);
  324.     // field length of the receiver is accessed without qualification
  325.     ... NotifyViews(this) ...
  326.     // receiver is accessed with the predeclared name this }
  327.  
  328. We believe that it is better to declare the receiver explicitly, which
  329. allows the programmer to choose a meaningful name for it (not just "this").
  330. The implicit passing of the receiver seems to be a little bit mysterious. We
  331. also believe that it contributes to the clarity of programs if fields of the
  332. receiver must always be qualified with the receiver's name. This is
  333. especially helpful if fields are accessed which are declared in the
  334. receiver's base type. In Oberon-2, Delete is therefore written in the
  335. following way:
  336.  
  337.   PROCEDURE (t: Text) Delete (from, to: LONGINT);
  338.   BEGIN
  339.     t^.length := t^.length - (to-from);
  340.     (* length is explicitly qualified with t *)
  341.     ... NotifyViews(t) ...
  342.     (* receiver has the user-defined name t *)
  343.   END Delete;
  344.  
  345.  
  346. MESSAGE RECORDS
  347.  
  348. Type-bound procedures are one way to implement messages. Another way is to
  349. take the phrase "sending a message" literally and to view a message as a
  350. packet of data that is sent to an object. This requires message records of
  351. various kinds and lengths and a handler per object that accepts all these
  352. message records. Type-extension provides these two mechanisms. Messages are
  353. extensible records and the handler is a procedure which takes a message as a
  354. parameter and interprets it according to the dynamic type of the message.
  355.  
  356. Consider a graphics editor. The objects in this application are various
  357. kinds of figures (rectangles, circles, lines, etc.) and the operations are
  358. drawing, moving, and filling the figures. For every operation a message
  359. record is declared which contains the arguments of the message as record
  360. fields:
  361.  
  362.   TYPE
  363.     Message   = RECORD END;
  364.     DrawMsg   = RECORD (Message) END;
  365.     MoveMsg   = RECORD (Message) dx, dy: INTEGER END;
  366.     FillMsg   = RECORD (Message) pat: Pattern END;
  367.  
  368. Next, the type Figure is declared, which contains the handler as a procedure
  369. variable:
  370.  
  371.   TYPE
  372.     Figure = POINTER TO FigureDesc;
  373.     FigureDesc = RECORD
  374.       x, y, width, height: INTEGER;
  375.       handle: PROCEDURE (f: Figure; VAR m: Message)
  376.     END;
  377.  
  378. The handler has two parameters: the receiver of the message (which is a
  379. Figure here) and the message itself. Since m is of type Message, all message
  380. types that are derived from it (DrawMsg, MoveMsg, etc.) are compatible.
  381. Note, that m is a variable parameter, so it may have a dynamic type which is
  382. an extension of its static type Message. Every extension of Figure (i.e.,
  383. Rectangle, Circle, Line) has its own handler that is installed in objects of
  384. this type. The handler for rectangle objects might look like this:
  385.  
  386.   PROCEDURE HandleRect (f: Figure; VAR m: Message);
  387.   BEGIN
  388.     WITH
  389.       m(DrawMsg) DO ... draw the rectangle f ...
  390.     | m(MoveMsg) DO ... move the rectangle f by (m.dx, m.dy) ...
  391.     | m(FillMsg) DO ... fill the rectangle f with m.pat ...
  392.     ELSE (* ignore the message *)
  393.     END
  394.   END HandleRect;
  395.  
  396. The With statement is a regional type guard. It has been extended in
  397. Oberon-2 to accept multiple variants. The above With statement should be
  398. read as follows: if the dynamic type of m is DrawMsg, then the statement
  399. sequence following the first DO symbol is executed and a type guard
  400. m(DrawMsg) is implicitly applied to every occurrence of m; else if the
  401. dynamic type of m is MoveMsg, then the statement sequence following the
  402. second DO symbol is executed where every occurrence of m is regarded as a
  403. MoveMsg; and so on. If no variant matches and if no else part is specified
  404. program execution is aborted. Using objects of type Figure requires the
  405. following actions:
  406.  
  407.   VAR f: Figure; r: Rectangle; move: MoveMsg;
  408.  
  409.   NEW(r); r^.handle := HandleRect;
  410.   (*initialize the object by installing the rectangle handler*)
  411.   ... f := r ...
  412.   move.dx := ...; move.dy := ...;  (*set up the message record*)
  413.   f.handle(f, move);  (*send the message*)
  414.       (*possibly retrieve output arguments from the message record*)
  415.  
  416. The use of message records has both advantages and disadvantages.
  417.  
  418.  
  419. Advantages
  420.  
  421. - The message can be stored in a variable and can be sent later on.
  422. - The same message can easily be distributed to more than one object
  423. (message broadcast). Consider the case where all figures have to be moved.
  424. With type-bound procedures, the caller would have to traverse the list of
  425. figures and send a Move message to every figure:
  426.  
  427.   f := firstFigure; WHILE f # NIL DO f.Move(dx, dy); f := f^.next END
  428.  
  429. The structure of the figure list must be known to the caller (which is not
  430. always the case) and the code for the list traversal is duplicated in every
  431. client. With message records one can implement the list traversal in a
  432. procedure Broadcast to which the message is passed as a parameter:
  433.  
  434.   PROCEDURE (lst: List) Broadcast (VAR m: Message);
  435.     VAR f: Figure;
  436.   BEGIN
  437.     f := lst^.first; WHILE f # NIL DO f.handle(f, m); f := f^.next END
  438.   END Broadcast;
  439.  
  440. This allows hiding the list structure and keeping the code for the list
  441. traversal in a single place.
  442.  
  443. - An object can be sent a message which it does not understand. It may
  444. ignore the message or delegate it to another object. For example, a Fill
  445. message can be broadcast to all figures although only rectangles and circles
  446. understand it, but not lines. With type-bound procedures this is not
  447. possible because the compiler checks if a message is understood by the
  448. receiver.
  449. - The handler can be replaced at run time, changing the behaviour of an
  450. object.
  451. - Message records can be declared in different modules. This allows adding
  452. new messages to a type when a new module is written.
  453.  
  454.  
  455. Disadvantages
  456.  
  457. - It is not immediately clear which operations belong to a type, i.e. which
  458. messages an object understands. To find that out, one has to know which
  459. handler is installed at run time and how this handler is implemented.
  460. - The compiler cannot check if a message is understood by an object. Faulty
  461. messages can be detected only at run time and may go undetected for months.
  462. - Messages are interpreted by the handler at run time and in sequential
  463. order. This is much slower than the dynamic binding mechanism of type-bound
  464. procedures, which requires only a table lookup with a constant offset.
  465. Message records are much like messages in Smalltalk [7], which are also
  466. interpreted at run time.
  467. - Sending a message (i.e., filling and unfilling message records) is
  468. somewhat clumsy.
  469.  
  470. In general, type-bound procedures are clearer and type-safe, while message
  471. records are more flexible. One should use type-bound procedures whenever
  472. possible. Message records should only be used where special flexibility is
  473. needed, e.g., for broadcasting a message or for cases where it is important
  474. to add new messages to a type later without changing the module that
  475. declares the type.
  476.  
  477.  
  478. PERSISTENT OBJECTS
  479.  
  480. Our implementation of Oberon-2 allows persistent objects. An object is
  481. called persistent if it outlives the program which created it. To make an
  482. object persistent, it must be possible to write it to a file and to
  483. reconstruct it from that external format. In Oberon-2, every record object
  484. carries a descriptor of its dynamic type. Among other things this descriptor
  485. contains the type name as a pair (module name, type name). It is possible to
  486. implement a procedure GetName which returns the type name of a given object,
  487. and a procedure New which creates and returns an object of a type specified
  488. by a type name.
  489.  
  490.   DEFINITION Objects;
  491.     PROCEDURE GetName(object: Object; VAR typeName: ARRAY OF CHAR);
  492.     PROCEDURE New(typeName: ARRAY OF CHAR; VAR object: Object);
  493.   END Objects.
  494.  
  495. If x is an extension of Object and understands a Load and a Store message,
  496. procedures to externalize and internalize x are (a Rider is a position in a
  497. file and is used to read and write data)
  498.  
  499.   PROCEDURE WriteObject(VAR r: Files.Rider; x: Object);
  500.     VAR name: ARRAY 64 OF CHAR;
  501.   BEGIN
  502.     Objects.GetName(x, name);
  503.     i := -1; REPEAT INC(i); Files.Write(r, name[i]) UNTIL name[i] = 0X;
  504.     IF x # NIL THEN x.Store(r) END  (* store fields of x to r *)
  505.   END WriteObject;
  506.  
  507.   PROCEDURE ReadObject(VAR r: Files.Rider; VAR x: Object);
  508.     VAR name: ARRAY 64 OF CHAR;
  509.   BEGIN
  510.     i := -1; REPEAT INC(i); Files.Read(r, name[i]) UNTIL name[i] = 0X;
  511.     Objects.New(name, x);
  512.     IF x # NIL THEN x.Load(r) END  (* read fields of x from r *)
  513.   END ReadObject;
  514.  
  515. More details on persistent objects as well as on optimization aspects can be
  516. found in [5].
  517.  
  518.  
  519. IMPLEMENTATION
  520.  
  521. In order to support object-oriented programming certain information about
  522. objects must be available at run time: The dynamic type of an object is
  523. needed for type tests and type guards. A table with the addresses of the
  524. type-bound procedures is needed for calling them using dynamic binding.
  525. Finally, the Oberon system has a garbage collector which needs to know the
  526. locations of pointers in dynamically allocated records. All this information
  527. is stored in so-called type descriptors of which there is one for every
  528. record type at run time.
  529.  
  530. The dynamic type of a record corresponds to the address of its type
  531. descriptor. For dynamically allocated records this address is stored in a
  532. so-called type tag which precedes the actual data and which is invisible for
  533. the programmer. If f is of dynamic type Rectangle (an extension of Figure),
  534. the run-time data structures are shown in Figure 2.
  535.  
  536.  
  537.  
  538.   Figure 2.  A variable f of dynamic type Rectangle, the record f points to,
  539.   and its type descriptor
  540.  
  541. Since both the table of procedure addresses and the table of pointer offsets
  542. must have a fixed offset from the type descriptor address, and since both
  543. may grow when the type is extended and further procedures or pointers are
  544. added, the tables are located at the opposite ends of the type descriptor
  545. and grow in different directions.
  546.  
  547. A message v.P is implemented as v^.tag^.ProcTab[Index-of-P]. The procedure
  548. table index Indexp is known for every type-bound procedure P at compile
  549. time. A type test of the form v IS T is translated into
  550. v^.tag^.BaseTypes[ExtensionLevel-of-T] = TypDescAdrT. Both the extension
  551. level of a record type and the address of its type descriptor are known at
  552. compile time. For example, the extension level of Figure is 0 (it has no
  553. base type), and the extension level of Rectangle is 1.
  554.  
  555. Type-bound procedures cause no memory overhead in objects (the type tag was
  556. already needed in Oberon-1). They cause only minimal run-time overhead
  557. compared to ordinary procedures. On a Ceres computer (NS32532 processor) a
  558. dynamically-bound procedure call is less than 10 % slower than a
  559. statically-bound call [3]. Measured over a whole program this difference is
  560. insignificant.
  561.  
  562. More details on the implementation of Oberon, particularly on the garbage
  563. collector, can be found in [4] and [5].
  564.  
  565.  
  566. AVAILABILITY
  567.  
  568. Oberon-2 was developed on the Ceres computer and has been ported to several
  569. other machines. Currently it is available on Sun's SparcStation, on
  570. Digital's DECstation, and on IBM's RS/6000. The compiler and the whole
  571. Oberon system (garbage collection, command activation, dynamic loading,
  572. etc.) is available from ETH without charge. It can be obtained via anonymous
  573. internet file transfer ftp (hostname: neptune.inf.ethz.ch, internet address:
  574. 129.132.101.33, directory: Oberon).
  575.  
  576.  
  577. ACKNOWLEDGEMENTS
  578.  
  579. Oberon-2 is the result of many discussions among the members of our
  580. institute. It was particularly influenced by the ideas of N.Wirth,
  581. J.Gutknecht, and J.Templ. The compiler and the system were ported to other
  582. machines by R.Crelier, J.Templ, M.Franz, and M.Brandis.
  583.  
  584.  
  585. REFERENCES
  586.  
  587. 1.  Wirth, N "The Programming Language Oberon" Software Practice and
  588.   Experience, Vol 18, No 7, (July 1988), pp 671-690.
  589. 2.  M„ssenb„ck, H "The Programming Language Oberon-2" Computer Science
  590.   Report 160, ETH Z…rich (May 1991).
  591. 3.  M„ssenb„ck, H and Templ, J "Object Oberon - A Modest Object-Oriented
  592.   Language" Structured Programming, Vol 10, No 4 (1989), pp 199-207.
  593. 4.  Wirth, N and Gutknecht, J "The Oberon System" Computer Science Report
  594.   88, ETH Z…rich (1988).
  595. 5.  Pfister, C and Heeb, B and Templ, J "Oberon Technical Notes" Computer
  596.   Science Report 156, ETH Z…rich (March 1991).
  597. 6.  Stroustrup, B "The C++ Programming Language" Addison-Wesley (1986).
  598. 7.  Goldberg, A and Robson, D "Smalltalk-80, The Language and its
  599.   Implementation", Addison-Wesley (1983).
  600. 8.  Tesler, L "Object-Pascal" Structured Language World, Vol 9, No 3,
  601.   (1985).
  602.